package net.maku.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SmUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.maku.api.module.system.SmsApi;
import net.maku.framework.common.constant.Constant;
import net.maku.framework.common.entity.WxUserInfoEntity;
import net.maku.framework.common.exception.MemberNotExistException;
import net.maku.framework.common.exception.ServerException;
import net.maku.framework.common.exception.WeiXinNotBindException;
import net.maku.framework.common.utils.WeiXinUtils;
import net.maku.framework.security.cache.TokenStoreCache;
import net.maku.framework.security.crypto.Sm2Util;
import net.maku.framework.security.mobile.MobileAuthenticationToken;
import net.maku.framework.security.third.ThirdAuthenticationToken;
import net.maku.framework.security.third.ThirdLogin;
import net.maku.framework.security.user.UserDetail;
import net.maku.framework.security.wx.WxUnionIdAuthenticationToken;
import net.maku.member.entity.MemberUserEntity;
import net.maku.member.service.MemberUserService;
import net.maku.member.service.MemberUserTokenService;
import net.maku.member.vo.MemberUserTokenVO;
import net.maku.system.entity.SysOrgEntity;
import net.maku.system.enums.LoginOperationEnum;
import net.maku.system.service.*;
import net.maku.system.vo.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

/**
 * 权限认证服务
 *
 * @author 阿沐 babamu@126.com
 * <a href="https://maku.net">MAKU</a>
 */
@Service
//@AllArgsConstructor
@Slf4j
public class SysAuthServiceImpl implements SysAuthService {

    @Autowired
    private  SysCaptchaService sysCaptchaService;
    @Autowired
    private  TokenStoreCache tokenStoreCache;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private SysLogLoginService sysLogLoginService;
    @Autowired
    private  SysUserService sysUserService;
    @Autowired
    private MemberUserTokenService memberUserTokenService;
    @Autowired
    private  SmsApi smsApi;
    @Autowired
    private MemberUserService memberUserService;
    @Value("${wx.appId}")
    private String appId;
    @Value("${wx.appSecret}")
    private String appSecret;
    @Autowired
    private SysOrgService sysOrgService;


    @Override
    public MemberUserTokenVO loginByAccount(SysAccountLoginVO login) {
        Authentication authentication;
        try {
            // 用户认证
            authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(login.getUsername(),Sm2Util.decrypt(login.getPassword())));
//            authentication = authenticationManager.authenticate(
//                    new UsernamePasswordAuthenticationToken(login.getUsername(),login.getPassword()));
        } catch (BadCredentialsException e) {
            log.error("用户名或密码错误",e);
            throw new ServerException("用户名或密码错误");
        }

        // 用户信息
        UserDetail user = (UserDetail) authentication.getPrincipal();
        String openId = user.getOpenId();

        String retry = login.getRetry();
        if(StringUtils.isBlank(retry)){
            WxUserInfoEntity userInfoEntity = WeiXinUtils.getWxUserInfo(login.getCode(), appId, appSecret);
            String openIdClient = userInfoEntity.getOpenId();
            //当前输入的账号没有绑定微信
            if(StringUtils.isBlank(openId)){
                throw new WeiXinNotBindException(405,"账号与微信未绑定",userInfoEntity);
            }
            //当前用户微信与登陆账号不是绑定关系
            if(!openIdClient.equals(openId)){
                throw new ServerException("账号与微信不是绑定关系无法登录");
            }
        }


        // 生成 accessToken
        MemberUserTokenVO memberUserTokenVO = memberUserTokenService.createToken(user.getId());

        // 保存用户信息到缓存
        tokenStoreCache.saveUser(memberUserTokenVO.getAccessToken(), user);

        return memberUserTokenVO;
    }

    @Override
    public MemberUserTokenVO loginByMobile(SysMobileLoginVO login) {
        Authentication authentication;
        try {
            // 用户认证
            authentication = authenticationManager.authenticate(
                    new MobileAuthenticationToken(login.getMobile(), login.getCode()));
        } catch (BadCredentialsException e) {
            throw new ServerException("手机号或验证码错误");
        }

        // 用户信息
        UserDetail user = (UserDetail) authentication.getPrincipal();

        // 生成 accessToken
        MemberUserTokenVO memberUserTokenVO = memberUserTokenService.createToken(user.getId());

        // 保存用户信息到缓存
        tokenStoreCache.saveUser(memberUserTokenVO.getAccessToken(), user);

        return memberUserTokenVO;
    }

    @Override
    public MemberUserTokenVO loginByThird(SysThirdCallbackVO login) {
        Authentication authentication;
        try {
            // 转换对象
            ThirdLogin thirdLogin = BeanUtil.copyProperties(login, ThirdLogin.class);

            // 用户认证
            authentication = authenticationManager.authenticate(new ThirdAuthenticationToken(thirdLogin));
        } catch (BadCredentialsException e) {
            throw new ServerException("第三方登录失败");
        }

        // 用户信息
        UserDetail user = (UserDetail) authentication.getPrincipal();

        // 生成 accessToken
        MemberUserTokenVO memberUserTokenVO = memberUserTokenService.createToken(user.getId());

        // 保存用户信息到缓存
        tokenStoreCache.saveUser(memberUserTokenVO.getAccessToken(), user);

        return memberUserTokenVO;
    }

    @Override
    public boolean sendCode(String mobile) {
        // 生成6位验证码
        String code = RandomUtil.randomNumbers(6);

        SysUserVO user = sysUserService.getByMobile(mobile);
        if (user == null) {
            throw new ServerException("手机号未注册");
        }

        // 发送短信
        return smsApi.sendCode(mobile, "code", code);
    }

    @Override
    public AccessTokenVO getAccessToken(String refreshToken) {
        MemberUserTokenVO memberUserTokenVO  = memberUserTokenService.refreshToken(refreshToken);

        // 封装 AccessToken
        AccessTokenVO accessToken = new AccessTokenVO();
        accessToken.setAccessToken(memberUserTokenVO.getAccessToken());
        accessToken.setAccessTokenExpire(memberUserTokenVO.getAccessTokenExpire());

        return accessToken;
    }

    @Override
    public void logout(String accessToken) {
        // 用户信息
        UserDetail user = tokenStoreCache.getUser(accessToken);

        // 删除用户信息
        tokenStoreCache.deleteUser(accessToken);

        if(user != null){
            // Token过期
            memberUserTokenService.expireToken(user.getId());

            // 保存登录日志
            sysLogLoginService.save(user.getUsername(), Constant.SUCCESS, LoginOperationEnum.LOGOUT_SUCCESS.getValue());
        }

    }

    @Override
    public void bindAccount(BindAccountVo bindAccountVo) {
        MemberUserEntity entity = memberUserService.getOne(new LambdaQueryWrapper<MemberUserEntity>().eq(MemberUserEntity::getMobile, bindAccountVo.getUsername()));
        String openId = entity.getOpenId();
        String unionId = bindAccountVo.getUnionId();
        String openIdClient = bindAccountVo.getOpenId();
        if(StringUtils.isNotBlank(openId)){
            if(openIdClient.equals(openId)){
                throw new ServerException("账号已经绑定微信，无需重复绑定");
            }else{
                throw new ServerException("账号已经绑定其他微信，无法绑定当前微信");
            }
        }
        if(StringUtils.isBlank(openId)){
            MemberUserEntity userEntity = memberUserService.getOne(new LambdaQueryWrapper<MemberUserEntity>().eq(MemberUserEntity::getOpenId, bindAccountVo.getOpenId()));
            if(userEntity != null){
                throw new ServerException("微信已绑定其他账号，请联系管理员");
            }

            MemberUserEntity update = new MemberUserEntity();
            update.setId(entity.getId());
            update.setOpenId(openIdClient);
            update.setUnionId(unionId);
            memberUserService.updateById(update);
        }

    }

    @Override
    public MemberUserTokenVO loginByUnionId(UnionLoginVO login) {
        QueryWrapper<MemberUserEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("union_id",login.getUnionId());
        MemberUserEntity userEntity = memberUserService.getOne(queryWrapper);
        Integer source = login.getSource();
        String phone = null;
        if(source.equals(UnionLoginVO.SOURCE_MINI)){
            if(StringUtils.isBlank(login.getCode())){
                throw new ServerException("code不能为空");
            }
            phone = WeiXinUtils.getUserPhone(login.getCode(), appId, appSecret);
        }else if(source.equals(UnionLoginVO.SOURCE_PUBLIC)){
            phone = login.getPhone();
        }else {
            throw new ServerException("请求来源参数错误");
        }

        if(StringUtils.isBlank(phone)){
            throw new ServerException("获取手机信息失败");
        }
        if(userEntity == null){
            String unionId = login.getUnionId();
            String openId = login.getOpenId();
            //1.如果根据union查询不到用户信息，则拿手机号去查询手机用户存在
            QueryWrapper<MemberUserEntity> phoneQuery = new QueryWrapper<>();
            phoneQuery.eq("mobile",phone);
            MemberUserEntity phoneUser = memberUserService.getOne(phoneQuery);
            if(phoneUser != null){
                //如果存在已有的手机用户则绑定手机用户信息
                MemberUserEntity update = new MemberUserEntity();
                update.setId(phoneUser.getId());
                update.setUnionId(unionId);
                if(source.equals(UnionLoginVO.SOURCE_MINI)){
                    update.setOpenId(openId);
                }
                if(source.equals(UnionLoginVO.SOURCE_PUBLIC)){
                    update.setPublicOpenId(openId);
                }
                memberUserService.updateById(update);
            }
            //2.如果根据union查询不到用户信息，则拿手机号去查询手机不用户存在
            if(phoneUser == null){
                throw new MemberNotExistException(406,"获取手机号失败",phone);
            }
        }
        Authentication authentication;
        try {
            // 用户认证
            authentication = authenticationManager.authenticate(
                    new WxUnionIdAuthenticationToken(login.getUnionId(), phone));
        } catch (UsernameNotFoundException e) {
            //用户没有绑定用户
            throw new ServerException("手机号或验证码错误");
        }

        // 用户信息
        UserDetail user = (UserDetail) authentication.getPrincipal();

        // 生成 accessToken
        MemberUserTokenVO memberUserTokenVO = memberUserTokenService.createToken(user.getId());

        // 保存用户信息到缓存
        tokenStoreCache.saveUser(memberUserTokenVO.getAccessToken(), user);

        return memberUserTokenVO;
    }

    @Override
    public WxUserInfoVO getWxUserInfo(String code) {
        WxUserInfoEntity userInfoEntity = WeiXinUtils.getWxUserInfo(code, appId, appSecret);
        if(userInfoEntity == null){
            throw new ServerException("获取用户微信信息失败");
        }
        WxUserInfoVO wxUserInfoVO = BeanUtil.copyProperties(userInfoEntity, WxUserInfoVO.class);
        return wxUserInfoVO;
    }

    @Override
    public String getAuthUrl() {
        return null;
    }

    @Override
    public WxUserInfoEntity register(RegisterVO registerVO) {
        QueryWrapper<MemberUserEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mobile",registerVO.getMobile());
        MemberUserEntity user = memberUserService.getOne(queryWrapper);
        WxUserInfoEntity userInfoEntity = null;
        if(user != null){
            userInfoEntity = new WxUserInfoEntity();
            userInfoEntity.setUnionId(user.getUnionId());
            userInfoEntity.setOpenId(user.getOpenId());
            if(StringUtils.isBlank(user.getUnionId())){
                MemberUserEntity update = new MemberUserEntity();
                update.setId(user.getId());
                if(StringUtils.isNotBlank(registerVO.getUnionId())){
                    update.setUnionId(registerVO.getUnionId());
                }else{
                    userInfoEntity = WeiXinUtils.getWxUserInfo(registerVO.getCode(), appId, appSecret);
                    if(userInfoEntity == null){
                        throw new ServerException("获取unionid信息异常");
                    }
                    update.setUnionId(userInfoEntity.getUnionId());
                    update.setOpenId(userInfoEntity.getOpenId());
                }
                memberUserService.updateById(update);
                userInfoEntity.setUnionId(update.getUnionId());
            }
            return userInfoEntity;
        }else{
            MemberUserEntity userEntity = new MemberUserEntity();
            userEntity.setMobile(registerVO.getMobile());
            userEntity.setRealName(registerVO.getMemberName());
            userEntity.setUnionId(registerVO.getUnionId());
            userEntity.setOpenId(registerVO.getOpenId());
            userEntity.setPublicOpenId(registerVO.getPublicOpenId());
            if(StringUtils.isNotBlank(registerVO.getCode())){
                userInfoEntity = WeiXinUtils.getWxUserInfo(registerVO.getCode(), appId, appSecret);
                userEntity.setUnionId(userInfoEntity.getUnionId());
                userEntity.setOpenId(userInfoEntity.getOpenId());
            }else {
                if(StringUtils.isBlank(registerVO.getUnionId())){
                    throw new ServerException("unionId 不能为空");
                }
                userInfoEntity = new WxUserInfoEntity();
                userInfoEntity.setUnionId(registerVO.getUnionId());
                userInfoEntity.setOpenId(registerVO.getOpenId());
            }
            userEntity.setOrgId(registerVO.getOrgId());
            memberUserService.save(userEntity);
        }

        return userInfoEntity;
    }

    @Override
    public LoginUserVo getWxUserMobile(String code) {
        String userPhone = WeiXinUtils.getUserPhone(code, appId, appSecret);
        QueryWrapper<MemberUserEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mobile",userPhone);
        MemberUserEntity one = memberUserService.getOne(queryWrapper);
        if(one == null){
            one = new MemberUserEntity();
            one.setMobile(userPhone);
            LoginUserVo loginUserVo = BeanUtil.copyProperties(one, LoginUserVo.class);
            return loginUserVo;
        }
        LoginUserVo loginUserVo = BeanUtil.copyProperties(one, LoginUserVo.class);
        Long orgId = one.getOrgId();
        SysOrgEntity orgEntity = sysOrgService.getById(orgId);
        loginUserVo.setOrgName(orgEntity.getUnionAbbreviation());
        loginUserVo.setMemberId(one.getId());
        return loginUserVo;
    }

    @Override
    public LoginUserVo getUserInfoByMobile(String mobile) {
        QueryWrapper<MemberUserEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mobile",mobile);
        MemberUserEntity one = memberUserService.getOne(queryWrapper);
        if(one == null){
            one = new MemberUserEntity();
            one.setMobile(mobile);
            LoginUserVo loginUserVo = BeanUtil.copyProperties(one, LoginUserVo.class);
            return loginUserVo;
        }
        LoginUserVo loginUserVo = BeanUtil.copyProperties(one, LoginUserVo.class);
        Long orgId = one.getOrgId();
        SysOrgEntity orgEntity = sysOrgService.getById(orgId);
        loginUserVo.setOrgName(orgEntity.getUnionAbbreviation());
        loginUserVo.setMemberId(one.getId());
        return loginUserVo;
    }
}
